import type { ReadonlyRequestCookies } from 'next/dist/server/web/spec-extension/adapters/request-cookies';

/**
 * 判断字符串是否为 JSON 格式
 * @param {string} str - 目标字符串
 */
export function isJSON(str: string) {
  try {
    JSON.parse(str);
    return true;
  } catch {
    return false;
  }
}

const parseThemeCookie = (themeCookie: string) => {
  let theme: 'dark' | 'light' | undefined;
  if (themeCookie && isJSON(themeCookie)) {
    const _theme = JSON.parse(themeCookie);
    if (_theme !== 'auto') {
      theme = _theme;
    }
  }
  return theme;
};

/**
 * 通过 request cookie 获取主题
 * @description 使用组件库时首屏一般是亮色的。这就会导致暗色模式下，用户的界面先显示亮色主题，再切换到暗色主题。用户感受上就是会 “闪” 一下，体验很差。这一类问题有一个专业术语叫做 FOUC 。解决方法是在服务端通过cookie获取用户的主题设置，并在首屏渲染时就应用该主题。这样可以避免首屏的 “闪” 现象。
 * @param cookiesStore - cookiesStore
 */
export const getRequestCookiesTheme = (cookiesStore: ReadonlyRequestCookies) => {
  const appThemeCookie = cookiesStore.get('appTheme')?.value;
  return parseThemeCookie(appThemeCookie ?? '');
};

/**
 * 复制文本到剪贴板（传统版本），如果浏览器不支持 Clipboard API，则使用 fallback 方法
 * @param text - 需要复制的文本
 */
export function fallbackCopyToClipboard(text: string) {
  return new Promise<void>((resolve, reject) => {
    const textarea = document.createElement('textarea');
    textarea.value = text;
    textarea.style.position = 'fixed';
    textarea.style.opacity = '0';
    document.body.appendChild(textarea);
    textarea.select();
    try {
      document.execCommand('copy');
      resolve();
    } catch (err) {
      reject(err);
    } finally {
      document.body.removeChild(textarea);
    }
  });
}

/**
 * 复制文本到剪贴板
 * @param text - 需要复制的文本
 */
export function copyToClipboard(text: string) {
  if (navigator.clipboard && window.isSecureContext) {
    return navigator.clipboard.writeText(text);
  } else {
    return fallbackCopyToClipboard(text);
  }
}

export function isTruthy<T>(value?: T | undefined | null | false): value is T {
  return Boolean(value);
}

/**
 * 清理对象中的 undefined 属性
 * @param obj - 需要清理的对象
 * @param includes - 包含的类型，默认为 ['undefined']
 */
export const objectClean = (
  obj: object,
  includes?: ('empty' | 'false' | 'undefined' | 'null')[]
) => {
  const _includes = includes ?? ['undefined'];
  return Object.fromEntries(
    Object.entries(obj).filter(([, value]) => {
      if (_includes.includes('undefined') && value === undefined) return false;
      if (_includes.includes('null') && value === null) return false;
      if (_includes.includes('false') && typeof value === 'boolean' && !value) return false;
      if (_includes.includes('empty') && typeof value === 'string' && value.trim() === '') {
        return false;
      }
      return true;
    })
  );
};
